package com.bonshotels.DomainLayer.DomainModel;

import java.io.Serializable;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;

import com.bonshotels.Excepcions.HotelsNoDisp;

/**
 * Classe Poblacio.
 * 
 * Aquesta classe representa lentitat poblacio. Una poblacio te un nom i un conjunt
 * dhotels que pot ser buit. Es public ja que el paquet de capa de dades ha de poder 
 * veure aquesta classe (que esta en un paquet diferent, Domain Model) per a poder 
 * fer el mapeig correctament. Com que java no te tuples hem implementat les tuples del 
 * disseny amb un Map (Diccionari) on la clau es de tipus String, i es correspon amb el 
 * nom de la variable de la tupla, i el valor es de tipus Object i per tant ens permet 
 * posar-hi qualsevol cosa. Per obtenir les dades de la tupla cal fer un get del valor 
 * passant-hi la clau (el nom de la variable) i fer un downcasting (conversio de tipus 
 * baixant en la jerarquia de classes) a la subclasse que ens interessa.
 * Com que utilitzem el framework de persistencia
 * Hibernate i aquest utilitza reflexio per fer la seva feina les nostres classes han d'acomplir una
 * serie de propietats perque la reflexio del Hibernate pugui fer completament la seva feina, les
 * classes han de ser publiques, han de tenir un constructor public buit (si no en tenen un d'explicit
 * el per defecte ja ho es), han d'implementar Serializable, i han de tenir substituits (override) els
 * metodes equals() i hashCode() perque identifiquin les classes a partir de la seva clau natural.
 * Com que Poblacio s'identifica pel seu nom, l'hem fet servir a l'hora
 * d'implementar equals(), hashCode() i els mapping de Hibernate.
 * 
 * @author Jordi Sabater
 */
public class Poblacio implements Serializable {



	/** La constant serialVersionUID. */
	private static final long serialVersionUID = -4607351354125869726L;

	/** El nom de la poblacio. */
	private String nom;
	
	/** Una poblacio te una associacio multiple navegable cap a la classe Hotel, es
	 * per aixo que hem dafegir aquest atribut com un conjunt (Set) dHotels. Aquest
	 * conjunt pot ser buit i per tant caldra fer les comprovacions necessaries per
	 * tal de no accedir a cap referencia nulla. */
	private Set<Hotel> hotel;
	
	/**
	 * Instancia una nova poblacio.
	 */
	public Poblacio() {
		hotel = new HashSet<Hotel>();
	}
	
	/**
	 * Obte el nom de la poblacio.
	 *
	 * @return El nom de la poblacio.
	 */
	public String obteNom() {
		return nom;
	}
	
	/**
	 * Per cada hotel que hi ha en la poblacio sobte la seva informacio i els tipus
	 * dhabitacions lliures de cada hotel amb la seva informacio. Un tipus dhabitacio
	 * lliure es aquell que per a la franja de dies compresos entre 'dIni' i 'dFi',
	 * hi ha alguna habitacio daquell tipus que no te cap reserva que solapi
	 * temporalment, i que a mes el numero de persones que caben en aquell tipus
	 * dhabitacio es mes gran o igual a numOcup. Com que hem implementat el bucle
	 * aprofitant que la classe Set implementa la interficie 'Iterable', no cal
	 * comprovar en cap moment si la poblacio te algun hotel o no, ja que en cas de
	 * que el conjunt estigui buit directament no farem cap iteracio en el bucle.
	 *
	 * @param dIni La data inicial per a la que es busca si hi ha habitacions lliures.
	 * @param dFi La data final per a la que es busca si hi ha habitacions lliures.
	 * @param numOcup El nombre docupants que sallotjaran en una habitacio.
	 * @return retorna un conjunt (Set) de tuples (Map) amb la seguent informacio: Per
	 * cada hotel de la poblacio sobte el seu nom (key: "nomHotel"), el nom de la
	 * categoria (key: "categoria"), la descripcio (key: "desc"), la seva avaluacio mitjana
	 * (key: "avaluacio"), i un conjunt on es pot consultar, per cada tipus dhabitacio del
	 * hotel que tingui alguna habitacio lliure per a les condicions de dates i nombre
	 * docupants passades com a parametres, el nom del tipus (key: "nomTipus"), el nombre
	 * dhabitacions lliures daquell tipus (key: "numeroDisp"), i el seu preu (key: "preu").
	 * @throws HotelsNoDisp the hotels no disp
	 */
	public Set<Map<String, Object> > buscarHabitacio(Date dIni, Date dFi, int numOcup) 
		  throws HotelsNoDisp {
		Set<Map<String, Object> > hotels = new HashSet<Map<String, Object> >();
		for(Hotel h : hotel) {
			Map<String, Object> hot = h.buscarHabitacio(dIni, dFi, numOcup);
			if ( !( (Set<Map<String,Object> >) hot.get("habs") ).isEmpty() ) {
				hotels.add(hot);
			}
		}
		if(hotels.isEmpty()) throw new HotelsNoDisp();
		return hotels;
	}

	/**
	 * Getter de nom.
	 *
	 * @return El nom
	 */
	public String getNom() {
		return nom;
	}

	/**
	 * Setter de nom.
	 *
	 * @param nom El nou nom
	 */
	public void setNom(String nom) {
		this.nom = nom;
	}

	/**
	 * Getter de hotel.
	 *
	 * @return El hotel
	 */
	public Set<Hotel> getHotel() {
		return hotel;
	}

	/**
	 * Setter de hotel.
	 *
	 * @param hotel El nou hotel
	 */
	public void setHotel(Set<Hotel> hotel) {
		this.hotel = hotel;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((nom == null) ? 0 : nom.hashCode());
		return result;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Poblacio other = (Poblacio) obj;
		if (nom == null) {
			if (other.nom != null)
				return false;
		} else if (!nom.equals(other.nom))
			return false;
		return true;
	}

	/**
	 * Getter de serialversionuid.
	 *
	 * @return El serialversionuid
	 */
	public static long getSerialversionuid() {
		return serialVersionUID;
	}
	
}
